package com.webapps.tvprogram.ui;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.GestureDetector.OnGestureListener;
import android.widget.Toast;

import com.webapps.tvprogram.R;
import com.webapps.tvprogram.data.local.Program;
import com.webapps.tvprogram.ui.GraphicObjectWithText.Coordinates;
import com.webapps.tvprogram.utils.GlobalInfo;

public class ImageScroller extends Activity {

    private DrawingPanel panel;
    private static final String LOG_TAG = "TvProgram.ImageScroller";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        prepareStartingList(getIntent().getExtras().getBoolean(MainActivity.PROGRAMS_FAV_OPT));
        panel.setImageScrollerActivity(this);
        setContentView(panel);
    }

    private void prepareStartingList(boolean onlyFavList) {
        List<Program> progs = MainActivity.getInstance().getProgramsData();
        List<Bitmap> bitmaps = new ArrayList<Bitmap>();
        List<String> names = new ArrayList<String>();
        Log.d(LOG_TAG, "prepareStartingList, pnlyFavList=" + onlyFavList);
        if (onlyFavList) {
            for (Iterator<String> iter = MainActivity.getInstance().getFavouritesList().iterator(); iter.hasNext();) {
                String favProgName = iter.next();
                for (Iterator<Program> iterator = progs.iterator(); iterator.hasNext();) {
                    Program program = iterator.next();
                    if (favProgName.equalsIgnoreCase(program.getName())) {
                        // add program as a favourite to the list
                        bitmaps.add(program.getIcon());
                        names.add(program.getName());
                    }
                }
            }
        } else {
            for (Iterator<Program> iterator = progs.iterator(); iterator.hasNext();) {
                Program program = iterator.next();
                bitmaps.add(program.getIcon());
                names.add(program.getName());
            }
        }
        panel = new DrawingPanel(this, bitmaps, names, onlyFavList);
    }

    @Override
    public boolean onTouchEvent(MotionEvent me) {
        if (me.getAction() == MotionEvent.ACTION_UP) {
            panel.onFingerUpEvent(me);
            return true;
        }
        return panel.getGestureScanner().onTouchEvent(me);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }
}

class DrawingPanel extends SurfaceView implements SurfaceHolder.Callback, LongPressInterface {

    private List<GraphicObjectWithText> mGraphics;
    private Paint mPaint;
    private PaintingThread mThread;
    List<Bitmap> mImages;
    List<String> mTexts;
    private final int mElementsVerticalGap = 35;
    private final int mTouchSensitivenessDelta = 3; // MotionEvent Y delta
    private int mSurfWidth;
    private int mSurfHeight;
    private GestureDetector mGestureScanner;
    private Bitmap mBackgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.scroller_bg);
    private final float mScreenCenterUpEdge = 0.5f;
    private final float mScreenCenterDownEdge = 0.6f;
    private final float mScreenCenterCenterEdge = 0.55f;
    private Context mContext;
    private long mFingerDownTapTime;
    private int mFingerDownTapYPos;
    private final int mTapInterval = 220;
    private int mLastClickedItemPosition = -1;
    private static final String LOG_TAG = "TvProgram.DrawingPanel";
    private LongPressHandler mLongPressHandler;
    private ImageScroller mImageScrollerActivity;
    private boolean mOnlyFavList;

    private DrawingPanel(Context context) {
        super(context);
    }

    public void setImageScrollerActivity(ImageScroller is) {
        mImageScrollerActivity = is;
    }

    public DrawingPanel(Context context, List<Bitmap> images, List<String> texts, boolean onlyFavList) {
        this(context);
        mContext = context;
        mOnlyFavList = onlyFavList;
        mGraphics = new ArrayList<GraphicObjectWithText>();
        mImages = images;
        mTexts = texts;
        getHolder().addCallback(this);
        setFocusable(true);
        mPaint = new Paint();
        mPaint.setColor(0xFFFFFF00);
        mLongPressHandler = new LongPressHandler(this);
        mGestureScanner = new GestureDetector(new OnGestureListener() {

            @Override
            public boolean onDown(MotionEvent me) {
                Log.v(LOG_TAG, "onDown");
                mFingerDownTapTime = me.getEventTime();
                mFingerDownTapYPos = (int) me.getY();
                if ((me.getY() >= mSurfHeight * mScreenCenterUpEdge && me.getY() <= mSurfHeight * (mScreenCenterDownEdge + 0.1f))) {
                    Log.v(LOG_TAG, "mLongPressHandler.prepareToHandle()");
                    mLongPressHandler.prepareToHandle();
                }
                return true;
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                Log.v(LOG_TAG, "onFling");
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                Log.v(LOG_TAG, "onLongPress");
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                // Log.v(LOG_TAG, "onScroll");
                mLongPressHandler.cancelLongPress();
                for (GraphicObjectWithText graphic : mGraphics) {
                    if (distanceY < -mTouchSensitivenessDelta || distanceY > mTouchSensitivenessDelta) {
                        graphic.moveBy(-(int) distanceY);
                        validateGObject(graphic);
                    }
                }
                return true;
            }

            @Override
            public void onShowPress(MotionEvent e) {
                Log.v(LOG_TAG, "onShowPress");
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                Log.v(LOG_TAG, "onSingleTapUp");
                return true;
            }
        });
    }

    private void moveToProgramLastPosition() {
        Log.v(LOG_TAG, "moveToProgramLastPosition = " + mLastClickedItemPosition);
        if (mLastClickedItemPosition > -1 && mGraphics.size() > mLastClickedItemPosition) {
            Log.v(LOG_TAG, "moveToPosition");
            GraphicObjectWithText g = mGraphics.get(mLastClickedItemPosition);
            int itemCenterPos = g.getCoordinates().getBY() + g.getGraphic().getHeight() / 2;
            int moveDelta = (int) (mSurfHeight * mScreenCenterCenterEdge) - itemCenterPos;
            for (GraphicObjectWithText graphic : mGraphics) {
                graphic.moveBy(moveDelta);
                validateGObject(graphic);
            }
        }
    }

    private void initResources(int width, int height) {
        int yp = width / 2;
        Iterator<Bitmap> iteratorB;
        Iterator<String> iteratorS;
        mGraphics.clear();
        for (iteratorB = mImages.iterator(), iteratorS = mTexts.iterator(); iteratorB.hasNext() && iteratorS.hasNext();) {
            String string = iteratorS.next();
            Bitmap bitmap = resizeBitmapToHeight(iteratorB.next());
            GraphicObjectWithText gObj = new GraphicObjectWithText(bitmap, string);
            gObj.getCoordinates().setBY(yp);
            yp += gObj.getGraphic().getHeight() + mElementsVerticalGap;
            validateGObject(gObj);
            mGraphics.add(gObj);
        }
        mBackgroundImage = Bitmap.createScaledBitmap(mBackgroundImage, mSurfWidth, mSurfHeight, false);
        moveToProgramLastPosition();
        moveItemsToGrid();
    }

    private Bitmap resizeBitmapToHeight(Bitmap original) {
        int computedLogosHeight = (int) ((mScreenCenterDownEdge - mScreenCenterUpEdge) * mSurfHeight);
        float scaleFactor = (float) original.getWidth() / (float) original.getHeight();
        int computedLogosWidth = (int) (computedLogosHeight * scaleFactor);
        Bitmap b = Bitmap.createScaledBitmap(original, computedLogosWidth, computedLogosHeight, false);
        if (b != null) {
            return b;
        } else {
            return original;
        }
    }

    public void onFingerUpEvent(MotionEvent me) {
        Log.v(LOG_TAG, "> onFingerUpEvent");
        mLongPressHandler.cancelLongPress();
        int tapDiff = 5;
        if (me.getEventTime() - mFingerDownTapTime < mTapInterval) {
            Log.v(LOG_TAG, "> onFingerUpEvent: quick tap at  Y=" + me.getY());
            if ((me.getY() >= mSurfHeight * mScreenCenterUpEdge && me.getY() <= mSurfHeight * (mScreenCenterDownEdge + 0.1f))
                    && (mFingerDownTapYPos >= me.getY() - tapDiff && mFingerDownTapYPos <= me.getY() + tapDiff)) {
                Log.v(LOG_TAG, "> onFingerUpEvent: hit center");
                startDesiredActivity();
            } else {
                moveItemsToGrid();
            }
        } else { //
            // if ((me.getY() >= mSurfHeight * mScreenCenterUpEdge && me.getY() <= mSurfHeight * (mScreenCenterDownEdge + 0.1f))
            // && (mFingerDownTapYPos >= me.getY() - tapDiff && mFingerDownTapYPos <= me.getY() + tapDiff)) {
            // Log.v(LOG_TAG, "> onFingerUpEvent: long press center");
            // }
            moveItemsToGrid();
        }
        Log.v(LOG_TAG, "< onFingerUpEvent");
    }

    private void startDesiredActivity() {
        Log.v(LOG_TAG, "checkIfShouldStartDesiredActivity");
        int centerElementPositionInList = findCurrentCenterItem();
        mLastClickedItemPosition = centerElementPositionInList;
        String clickedStationName = mGraphics.get(centerElementPositionInList).getText();
        Intent intent = new Intent(mContext, SingleProgramList.class);
        intent.putExtra(GlobalInfo.BUNDLE_PROGRAM_NAME, clickedStationName);
        mContext.startActivity(intent);
    }

    private int findCurrentCenterItem() {
        int[] deltaToCenterY = new int[mGraphics.size()];
        int i = 0;
        for (GraphicObjectWithText graphic : mGraphics) {
            deltaToCenterY[i] = Math.abs((graphic.getCoordinates().getBY() + graphic.getGraphic().getHeight() / 2)
                    - (int) (mSurfHeight * mScreenCenterCenterEdge));
            ++i;
        }
        return findMinPosInTable(deltaToCenterY);
    }

    private int findMinPosInTable(int[] table) {
        if (table == null || table.length == 0) {
            return -1;
        }
        int smallest = table[0];
        int smallestIndex = 0;
        for (int i = 0; i < table.length; i++) {
            if (table[i] < smallest) {
                smallest = table[i];
                smallestIndex = i;
            }
        }
        return smallestIndex;
    }

    private void moveItemsToGrid() {
        Log.v(LOG_TAG, "moveItemsToGrid");
        int[] deltaToCenterY = new int[mGraphics.size()];
        int i = 0;
        for (GraphicObjectWithText graphic : mGraphics) {
            deltaToCenterY[i] = Math.abs((graphic.getCoordinates().getBY() + graphic.getGraphic().getHeight() / 2)
                    - (int) (mSurfHeight * mScreenCenterCenterEdge));
            ++i;
        }
        int centerElementPositionInList = findMinPosInTable(deltaToCenterY);
        Log.v(LOG_TAG, "nearest center element index = " + centerElementPositionInList);
        GraphicObjectWithText g = mGraphics.get(centerElementPositionInList);
        int bitmapCenter = g.getCoordinates().getBY() + g.getGraphic().getHeight() / 2;
        int moveVal = 0;
        if (bitmapCenter > mSurfHeight * mScreenCenterCenterEdge) {
            moveVal = -deltaToCenterY[centerElementPositionInList];
        } else if (bitmapCenter < mSurfHeight * mScreenCenterCenterEdge) {
            moveVal = deltaToCenterY[centerElementPositionInList];
        }
        for (GraphicObjectWithText graphic : mGraphics) {
            graphic.moveBy(moveVal);
            validateGObject(graphic);
        }
    }

    private void validateGObject(GraphicObjectWithText gObj) {
        // validate horizontal position
        int yp = gObj.getCoordinates().getBY();
        int hCenterDelta = Math.abs(mSurfHeight / 2 - yp);
        float hCenterDeltaPercent = (float) hCenterDelta / (float) mSurfHeight;
        int horizontalDelta = (int) (mSurfWidth / 2 * hCenterDeltaPercent);
        gObj.getCoordinates().setBX(mSurfWidth / 3 - horizontalDelta * 2);
        // validate transparency
        int transparency = 255 - horizontalDelta * 3;
        if (transparency < 0) {
            transparency = 0;
        } else if (transparency > 255) {
            transparency = 255;
        }
        gObj.setPaintsTransparency(transparency);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.v(LOG_TAG, "surfaceCreated");
        mSurfHeight = holder.getSurfaceFrame().height();
        mSurfWidth = holder.getSurfaceFrame().width();
        initResources(mSurfWidth, mSurfHeight);
        mThread = new PaintingThread(this.getHolder(), this);
        mThread.setRunning(true);
        mThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.v(LOG_TAG, "surfaceChanged");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.v(LOG_TAG, "surfaceDestroyed");
        mThread.setRunning(false);
    }

    @Override
    public void onDraw(Canvas canvas) {
        drawBackground(canvas);
        Bitmap bitmap;
        String text;
        Coordinates coords;
        for (GraphicObjectWithText graphic : mGraphics) {
            bitmap = graphic.getGraphic();
            text = graphic.getText();
            coords = graphic.getCoordinates();
            canvas.drawBitmap(bitmap, coords.getBX(), coords.getBY(), graphic.getPaint());
            canvas.drawText(text, coords.getSX(), coords.getSY(), graphic.getTextPaint());
        }
    }

    private void drawBackground(Canvas canvas) {
        // canvas.drawBitmap(mBackgroundImage, 0, 0, null);
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(new Rect(0, 0, mSurfWidth, (int) (mScreenCenterUpEdge * mSurfHeight)), mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas
                .drawRect(new Rect(0, (int) (mScreenCenterUpEdge * mSurfHeight), mSurfWidth, (int) (mScreenCenterDownEdge * mSurfHeight)), mPaint);
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(new Rect(0, (int) (mScreenCenterDownEdge * mSurfHeight), mSurfWidth, mSurfHeight), mPaint);
    }

    public GestureDetector getGestureScanner() {
        return mGestureScanner;
    }

    public PaintingThread getPaintingThread() {
        return mThread;
    }

    @Override
    public void reactOnLongPress() {
        Log.v(LOG_TAG, "reactOnLongPress");
        int centerElementPositionInList = findCurrentCenterItem();
        final String clickedStationName = mGraphics.get(centerElementPositionInList).getText();
        boolean adding = true;
        Resources r = mContext.getResources();
        final CharSequence[] items = new CharSequence[1];
        if (!MainActivity.getInstance().isOnFavouritesList(clickedStationName)) {
            items[0] = r.getString(R.string.fav_dial_add);
        } else {
            items[0] = r.getString(R.string.fav_dial_remove);
            adding = false;
        }
        final boolean finalAdding = adding;
        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        builder.setTitle(r.getString(R.string.fav_dial_title) + " " + clickedStationName);
        builder.setItems(items, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface dialog, int item) {
                if (finalAdding) {
                    Log.v(LOG_TAG, "addToFavourites=" + clickedStationName);
                    MainActivity.getInstance().addToFavourites(clickedStationName);
                } else {
                    Log.v(LOG_TAG, "removeFromFavourites=" + clickedStationName);
                    MainActivity.getInstance().removeFromFavourites(clickedStationName);
                    if (!mOnlyFavList) {
                        return;
                    }
                    int indexToRemove = -1;
                    int i = 0;
                    for (Iterator<GraphicObjectWithText> iterator = mGraphics.iterator(); iterator.hasNext();) {
                        GraphicObjectWithText obj = iterator.next();
                        if (obj.getText().equalsIgnoreCase(clickedStationName)) {
                            indexToRemove = i;
                            break;
                        }
                        ++i;
                    }
                    Log.v(LOG_TAG, "removeFromFavourites, indexToRemove=" + indexToRemove);
                    if (indexToRemove >= 0) {
                        mGraphics.remove(indexToRemove);
                        mImages.remove(indexToRemove);
                        mTexts.remove(indexToRemove);
                        if (mGraphics.size() == 0) {
                            Toast.makeText(MainActivity.getInstance().getApplicationContext(), mContext
                                    .getResources()
                                    .getString(R.string.image_scroller_remove_last_fav_item_error), Toast.LENGTH_LONG).show();
                            mImageScrollerActivity.finish();
                            return;
                        }
                        int yp = mSurfWidth / 2;
                        for (Iterator<GraphicObjectWithText> iterator = mGraphics.iterator(); iterator.hasNext();) {
                            GraphicObjectWithText gObj = iterator.next();
                            gObj.getCoordinates().setBY(yp);
                            yp += gObj.getGraphic().getHeight() + mElementsVerticalGap;
                            validateGObject(gObj);
                        }
                        moveItemsToGrid();
                        invalidate();
                    }
                }
            }
        });
        AlertDialog alert = builder.create();
        alert.show();
    }
}

class LongPressHandler extends Handler {

    private static String LOG_TAG = "LongPressHandler";
    private long mDelayMillis = 500;
    private LongPressInterface mLongPressInterface;

    public LongPressHandler(LongPressInterface interf) {
        mLongPressInterface = interf;
    }

    @Override
    public void handleMessage(Message msg) {
        Log.v(LOG_TAG, "handleMessage");
        mLongPressInterface.reactOnLongPress();
    }

    public void prepareToHandle() {
        Log.v(LOG_TAG, "prepareToHandle");
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), mDelayMillis);
    }

    public void cancelLongPress() {
        if (this.hasMessages(0)) {
            Log.v(LOG_TAG, "cancelLongPress");
            this.removeMessages(0);
        }
    }
}

interface LongPressInterface {

    public void reactOnLongPress();
}

class GraphicObjectWithText {

    private Bitmap mBitmap;
    private Coordinates mCoordinates;
    private String mText;
    private Paint mPaint;
    private Paint mTextPaint;
    private final int mTextSize = 30;
    private final int mTextBitmapGap = 5;

    public GraphicObjectWithText(Bitmap bitmap, String text) {
        mBitmap = bitmap;
        mCoordinates = new Coordinates(10, 10);
        mText = text;
        mPaint = new Paint();
        mTextPaint = new Paint();
        mTextPaint.setColor(Color.RED);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setStyle(Paint.Style.STROKE);
    }

    public Bitmap getGraphic() {
        return mBitmap;
    }

    public void setGraphic(Bitmap bmp) {
        mBitmap = bmp;
    }

    public Coordinates getCoordinates() {
        return mCoordinates;
    }

    public void setCoordinates(int x, int y) {
        mCoordinates.setBX(x);
        mCoordinates.setBY(y);
    }

    public void moveLeft(int byValue) {
        mCoordinates.setBX(mCoordinates.bx - byValue);
    }

    public void moveRight(int byValue) {
        mCoordinates.setBX(mCoordinates.bx + byValue);
    }

    public void moveUp(int byValue) {
        mCoordinates.setBY(mCoordinates.by - byValue);
    }

    public void moveDown(int byValue) {
        mCoordinates.setBY(mCoordinates.by + byValue);
    }

    public void moveBy(int byValue) {
        mCoordinates.setBY(mCoordinates.by + byValue);
    }

    public String getText() {
        return mText;
    }

    public boolean isInArea(int vertY0, int vertY1) {
        // if()
        return false;
    }
    /**
     * Contains the coordinates of the graphic.
     */
    class Coordinates {

        private int bx, by, sx, sy;

        public Coordinates(int x, int y) {
            this.bx = x;
            this.by = y;
            sx = bx + mBitmap.getWidth() + mTextBitmapGap;
            sy = by + mBitmap.getHeight() / 2 + mTextSize;
        }

        public int getBX() {
            return bx;
        }

        public void setBX(int value) {
            bx = value;
            sx = bx + mBitmap.getWidth() + mTextBitmapGap;
        }

        public int getBY() {
            return by;
        }

        public void setBY(int value) {
            by = value;
            sy = by + mBitmap.getHeight() / 2 + mTextSize * 3 / 8;
        }

        public int getSX() {
            return sx;
        }

        public int getSY() {
            return sy;
        }
    }

    public Paint getPaint() {
        return mPaint;
    }

    public Paint getTextPaint() {
        return mTextPaint;
    }

    public void setPaintsTransparency(int alphaValue) {
        mPaint.setAlpha(alphaValue);
        mTextPaint.setAlpha(alphaValue);
    }
}

class PaintingThread extends Thread {

    private SurfaceHolder _surfaceHolder;
    private DrawingPanel _panel;
    private boolean _run = false;

    public PaintingThread(SurfaceHolder surfaceHolder, DrawingPanel panel) {
        _surfaceHolder = surfaceHolder;
        _panel = panel;
    }

    public void setRunning(boolean run) {
        _run = run;
    }

    public SurfaceHolder getSurfaceHolder() {
        return _surfaceHolder;
    }

    @Override
    public void run() {
        Canvas c;
        while (_run) {
            // Log.v("PaintingThread", "while run");
            c = null;
            try {
                c = _surfaceHolder.lockCanvas(null);
                synchronized (_surfaceHolder) {
                    _panel.onDraw(c);
                }
            } finally {
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) {
                    _surfaceHolder.unlockCanvasAndPost(c);
                }
            }
            try {
                Thread.sleep(GlobalInfo.SURFACE_REFRESH_RATE);
            } catch (InterruptedException e) {
            }
        }
        Log.v("PaintingThread", "run finished");
    }
}
